package durak.server.control;

import java.util.ArrayList;

import durak.client.view.PlayerView;
import durak.common.control.GameCommand;
import durak.common.model.Card;
import durak.common.model.Cards;
import durak.common.model.GameTableState;
import durak.server.model.Player;

/**
 * Command handler class is responsible for initializing and running the game -
 * logic wise communication initialization and running method is implemented
 * elsewhere
 */

public class CommandHandler {

	static public enum GameState {
		GameStateWaitingForAttack, GameStateWaitingForDefend, GameStateWaitingForCoAttack
	}

	Game gameSession;
	private GameState state; // keeping game state for logging usage in the future
	private boolean currentAttackerPassed;

	public CommandHandler(Game gameSession) {
		this.gameSession = gameSession;
	}

	void dealCards() {
		for (Player player : gameSession.getGamingTable().getPlayersList())
			for (int i = 0; i < 6; i++)
				player.getHand().addCard(gameSession.getGamingTable().getDeck().drawFromDeck());

	}

	/**
	 * runGame responsible for running the game
	 * 
	 * implementing the game logic of processing the clients response of
	 * attack\defend\withdraw
	 * 
	 * @param gamingTable
	 */
	public void runGame() {
		gameSession.getGamingTable().getDeck().shuffle();
		dealCards();
		gameSession.getGamingTable().setKozar(gameSession.getGamingTable().getDeck().getFirstCard());
		gameSession.getGamingTable().getDeck().removeCard(gameSession.getGamingTable().getKozar());
		gameSession.getGamingTable().getDeck().addCard(gameSession.getGamingTable().getKozar());
		PlayerControl attacker = ChooseFirstPlayer();
		gameSession.getGamingTable().setCurrentAttacker(attacker.getPlayer());
		gameSession.getGamingTable().setCurrentDefender(getDefenderForAttacker(attacker.getPlayer()));
		notifyGameState();
		attacker.attack();
		state = GameState.GameStateWaitingForAttack;
	}

	private void notifyGameState() {
		GameTableState gameState = new GameTableState();
		gameSession.getGamingTable().updateGameTableState(gameState);
		for (PlayerControl player : gameSession.getPlayers()) {
			player.notifyGameState(gameState);
		}
	}

	private Player getDefenderForAttacker(Player attacker) {
		Player defender = gameSession.getGamingTable().getNextPlayer(attacker);
		while (defender != null && !defender.isPlaying()) {
			defender = gameSession.getGamingTable().getNextPlayer(defender);
		}
		if (defender != attacker)
			return defender;
		return null;
	}

	private PlayerControl ChooseFirstPlayer() {
		PlayerControl firstPlayer = null;
		Card kozar = new Card(Card.Rank.ACE, gameSession.getGamingTable().getKozar().getSuit());
		for (PlayerControl player : gameSession.getPlayers()) {
			if (player.getPlayer().getHand().getLowestKozar(kozar).getRank().compareTo(kozar.getRank()) <= 0)
			{
					firstPlayer = player;
			}
		}
		return (firstPlayer != null) ? firstPlayer : gameSession.getPlayers().get(0);
	}

	void responseToAttackAction(PlayerControl attacker, Cards cards) {
		gameSession.getGamingTable().getCardsOnTheTable().addAttackCards(cards);
		attacker.getPlayer().getHand().removeCards(cards);
		notifyGameState();
		gameSession.getPlayerControl(gameSession.getGamingTable().getCurrentDefender()).defend(cards);
		state = GameState.GameStateWaitingForDefend;
		if(gameSession.getGamingTable().getCardsOnTheTable().getAttackCards().size() == 6) {
			notifyAllTableFull();
		}
	}

	private void notifyAllTableFull() {
		for (PlayerControl player : gameSession.getPlayers()) {
			if(gameSession.getGamingTable().getCurrentAttacker().getName().equals(player.getName()) ||
					gameSession.getGamingTable().getCoAttacker().getName().equals(player.getName()))
				player.notifyGameTableFull();
		}
	}

	void responseToDefendAction(PlayerControl defender, Cards cards) {
		gameSession.getGamingTable().getCardsOnTheTable().addDefendCards(cards);
		defender.getPlayer().getHand().removeCards(cards);
		notifyGameState();
		if (defender.getPlayer().getHand().isEmpty() || gameSession.getGamingTable().getCardsOnTheTable().getAttackCards().size() == 6) {
			/*
			 * Round finished First cleanup table and pass the cards to bita
			 */
			gameSession.getGamingTable().getBita().addCards(gameSession.getGamingTable().getCardsOnTheTable().retrieveCards());
			endOfRound();
			startNewRound(gameSession.getPlayerControl(gameSession.getGamingTable().getNextAttacker()));
		} else {
			if (gameSession.getGamingTable().getCoAttacker() == null &&
				gameSession.getGamingTable().getNextPlayer(defender.getPlayer()) != gameSession.getGamingTable().getCurrentAttacker() &&
				gameSession.getGamingTable().getNextPlayer(defender.getPlayer()).isPlaying()) {
				gameSession.getGamingTable().setCoAttacker(gameSession.getGamingTable().getNextPlayer(defender.getPlayer()));
			}
			gameSession.getPlayerControl(gameSession.getGamingTable().getCurrentAttacker()).attack();
			if (gameSession.getGamingTable().getCoAttacker() != null)
				gameSession.getPlayerControl(gameSession.getGamingTable().getCoAttacker()).attack();
			state = GameState.GameStateWaitingForAttack;
		}
	}

	void withdrawAction(PlayerControl playerControl) {
		playerControl.getPlayer().getHand().addCards(gameSession.getGamingTable().getCardsOnTheTable().retrieveCards());
		endOfRound();
		startNewRound(gameSession.getPlayerControl(getDefenderForAttacker(playerControl.getPlayer())));
	}

	private void startNewRound(PlayerControl currentAttacker) {
		gameSession.getGamingTable().setCurrentAttacker(currentAttacker.getPlayer());
		gameSession.getGamingTable().setCurrentDefender(getDefenderForAttacker(currentAttacker.getPlayer()));
		gameSession.getGamingTable().setCoAttacker(null);
		currentAttackerPassed = false;
		notifyRoundEnded();
		notifyGameState();
		currentAttacker.attack();
		state = GameState.GameStateWaitingForAttack;
//		notifyGameState();
	}

	void passAttackAction(PlayerControl playerControl) {
		if (gameSession.getGamingTable().getCoAttacker() != null && playerControl.getPlayer() == gameSession.getGamingTable().getCoAttacker()) {
			gameSession.getGamingTable().setCoAttacker(null);
		}
		if (playerControl.getPlayer() == gameSession.getGamingTable().getCurrentAttacker()) {
			currentAttackerPassed = true;
		}
		if (currentAttackerPassed && gameSession.getGamingTable().getCoAttacker() == null) {
			gameSession.getGamingTable().getBita().addCards(gameSession.getGamingTable().getCardsOnTheTable().retrieveCards());
			endOfRound();
			startNewRound(gameSession.getPlayerControl(gameSession.getGamingTable().getNextAttacker()));
		}
	}

	void endOfRound() {
		if(gameSession.getGamingTable().getDeck().isEmpty()) {
			int playerWithCards = 0;
			for(Player player : gameSession.getGamingTable().getPlayersList()){
				if(!player.getHand().isEmpty())
					playerWithCards++;
				else
					player.setPlaying(false);
			}
			if(playerWithCards <= 1) {
				System.out.println("End of game");
				
				/*
				 * Increment the number of loses for the loser
				 */
				if(playerWithCards == 1) {
					for(Player player : gameSession.getGamingTable().getPlayersList()){
						if(!player.getHand().isEmpty())
							player.incrementLoses();
					}
				}
				
				notifyEndOfGame();
				gameSession.getGamingTable().resetGameTable();
				try {
					Thread.sleep(5000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				runGame();
				return;
			}
		}
		
		/*
		 * Game still on
		 */
		ArrayList<Player> playersThatNeedCards = new ArrayList<Player>();
		playersThatNeedCards.add(gameSession.getGamingTable().getCurrentAttacker());
		playersThatNeedCards.add(gameSession.getGamingTable().getCurrentDefender());
		if (getDefenderForAttacker(gameSession.getGamingTable().getCurrentDefender()) != gameSession.getGamingTable().getCurrentAttacker())
			playersThatNeedCards.add(getDefenderForAttacker(gameSession.getGamingTable().getCurrentDefender()));
		for (Player player : playersThatNeedCards) {
			while (!gameSession.getGamingTable().getDeck().isEmpty() && player.getHand().size() < 6) {
				player.getHand().addCard(gameSession.getGamingTable().getDeck().DrawFirstCard());
			}
		}
	}

	private void notifyRoundEnded() {
		GameTableState gameState = new GameTableState();
		gameSession.getGamingTable().updateGameTableState(gameState);
		for (PlayerControl player : gameSession.getPlayers()) {
			player.notifyRoundEnded(gameState);
		}
	}

	private void joinPlayer(PlayerControl playerControl, String name) {
		playerControl.getPlayer().setName(name);
		playerControl.setInitialized(true);
	}

	void handleReceivedMessageFromClient(PlayerControl playerControl, GameCommand command) {
		// commands that server get from client
		PlayerView view = new PlayerView();
		view.viewCommandRecievedFromClient(command, playerControl.getPlayer().getName());
		switch (command.getCommandType()) {
		case C2S_ATTACKED:
			// gameSession.getGamingTable().getCardsOnTheTable().addAttackCards(command.getCards());
			// playerControl.getPlayer().getHand().removeCards(command.getCards());
			responseToAttackAction(playerControl, command.getCards());
			break;

		case C2S_DEFENDED:
			responseToDefendAction(playerControl, command.getCards());
			break;

		case C2S_WITHDRAWN:
			withdrawAction(playerControl);
			break;

		case C2S_PASS_ATTACK:
			passAttackAction(playerControl);
			break;
		case C2S_JOIN:
			joinPlayer(playerControl, command.getPlayerName());
			break;
		case C2S_QUIT:
			quitPLayer(playerControl);
		default:
			break;
		}
	}

	private void quitPLayer(PlayerControl playerControl) {
		int numOfActivePlayers = 0;
		playerControl.getPlayer().setPlaying(false);
		playerControl.getPlayer().setLeftTheGame(true);
		for(Player player : gameSession.getGamingTable().getPlayersList()) {
			if(!player.isLeftTheGame())
				numOfActivePlayers++;
		}
	/*	if(numOfActivePlayers < 1)
			gameSession.getServer2ClientsCommunication().gameOn = false;*/
	}

	private void notifyEndOfGame() {
		GameTableState gameState = new GameTableState();
		gameSession.getGamingTable().updateGameTableState(gameState);
		for (PlayerControl player : gameSession.getPlayers()) {
			player.notifyGameEnded(gameState);
		}
	}

}
